{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 05. Lenses and analysing lensing actions\n",
    "\n",
    "submitted by [substancia](https://github.com/substancia), adapted by [flaport](https://github.com/flaport)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Imports"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "import fdtd\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Grid"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "grid = fdtd.Grid(shape=(260, 15.5e-6, 1), grid_spacing=77.5e-9)\n",
    "# x boundaries\n",
    "grid[0:10, :, :] = fdtd.PML(name=\"pml_xlow\")\n",
    "grid[-10:, :, :] = fdtd.PML(name=\"pml_xhigh\")\n",
    "# y boundaries\n",
    "grid[:, 0:10, :] = fdtd.PML(name=\"pml_ylow\")\n",
    "grid[:, -10:, :] = fdtd.PML(name=\"pml_yhigh\")\n",
    "simfolder = grid.save_simulation(\"Lenses\")  # initializing environment to save simulation data\n",
    "print(simfolder)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Objects\n",
    "\n",
    "defining a biconvex lens"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x, y = np.arange(-200, 200, 1), np.arange(190, 200, 1)\n",
    "X, Y = np.meshgrid(x, y)\n",
    "lens_mask = X ** 2 + Y ** 2 <= 40000\n",
    "for j, col in enumerate(lens_mask.T):\n",
    "    for i, val in enumerate(np.flip(col)):\n",
    "        if val:\n",
    "            grid[30 + i : 50 - i, j - 100 : j - 99, 0] = fdtd.Object(permittivity=1.5 ** 2, name=str(i) + \",\" + str(j))\n",
    "            break"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Source\n",
    "using a continuous source (not a pulse)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "grid[15, 50:150, 0] = fdtd.LineSource(period=1550e-9 / (3e8), name=\"source\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Detectors\n",
    "using a BlockDetector"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "grid[80:200, 80:120, 0] = fdtd.BlockDetector(name=\"detector\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Saving grid geometry for future reference"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "with open(os.path.join(simfolder, \"grid.txt\"), \"w\") as f:\n",
    "    f.write(str(grid))\n",
    "    wavelength = 3e8/grid.source.frequency\n",
    "    wavelengthUnits = wavelength/grid.grid_spacing\n",
    "    GD = np.array([grid.x, grid.y, grid.z])\n",
    "    gridRange = [np.arange(x/grid.grid_spacing) for x in GD]\n",
    "    objectRange = np.array([[gridRange[0][x.x], gridRange[1][x.y], gridRange[2][x.z]] for x in grid.objects], dtype=object).T\n",
    "    f.write(\"\\n\\nGrid details (in wavelength scale):\")\n",
    "    f.write(\"\\n\\tGrid dimensions: \")\n",
    "    f.write(str(GD/wavelength))\n",
    "    f.write(\"\\n\\tSource dimensions: \")\n",
    "    f.write(str(np.array([grid.source.x[-1] - grid.source.x[0] + 1, grid.source.y[-1] - grid.source.y[0] + 1, grid.source.z[-1] - grid.source.z[0] + 1])/wavelengthUnits))\n",
    "    f.write(\"\\n\\tObject dimensions: \")\n",
    "    f.write(str([(max(map(max, x)) - min(map(min, x)) + 1)/wavelengthUnits for x in objectRange]))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Simulation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from IPython.display import clear_output # only necessary in jupyter notebooks\n",
    "for i in range(400):\n",
    "    grid.step()  # running simulation 1 timestep a time and animating\n",
    "    if i % 10 == 0:\n",
    "        # saving frames during visualization\n",
    "        grid.visualize(z=0, animate=True, index=i, save=True, folder=simfolder)\n",
    "        plt.title(f\"{i:3.0f}\")\n",
    "        clear_output(wait=True) # only necessary in jupyter notebooks\n",
    "\n",
    "grid.save_data()  # saving detector readings"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can generate a video with ffmpeg:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "try:\n",
    "    video_path = grid.generate_video(delete_frames=False)  # rendering video from saved frames\n",
    "except:\n",
    "    video_path = \"\"\n",
    "    print(\"ffmpeg not installed?\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "if video_path:\n",
    "    from IPython.display import Video\n",
    "    display(Video(video_path, embed=True))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Analyse\n",
    "analysing data stored by above simulation by plotting a 2D decibel map"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "df = np.load(os.path.join(simfolder, \"detector_readings.npz\"))\n",
    "fdtd.dB_map_2D(df[\"detector (E)\"])"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
